package Population;

import java.awt.Color;
import java.util.Random;

import Friend.FriendArray;
import Stats.Stats;
import World.*;


public class Inhabitant {
	int ID;
	public boolean isAlive;
	int x_pos;
	int y_pos;
	String type = null;
	Color color = null;
	String groupName = "";
	
	int parentID;
	
	FriendArray friends;
	FriendArray associatedFriends;

	Stats inhabStats = new Stats();
	
	public Inhabitant(int x, int y, String t, Color c, int id, int parent)
	{
		
		friends = new FriendArray();
		associatedFriends = new FriendArray();

		x_pos=x;
		y_pos=y;
		isAlive = true;
		type = t;
		color = c;
		ID = id;
		parentID = parent;
	}
	

	public int getID()
	{
		return ID;
	}
	public int getX()
	{
		return x_pos;
	}
	public int getY()
	{
		return y_pos;
	}
	public boolean getLife()
	{
		return isAlive;
	}
	public int hp()
	{
		return inhabStats.hp;
	}
	public String getType()
	{
		return type;
	}
	public Color getColor()
	{
		return color;
	}
	public int getAge()
	{
		return inhabStats.age;
	}
	
	public void incAge()
	{
		inhabStats.age++;
	}
	public int getParentID()
	{
		return parentID;
	}
	public FriendArray getFriends()
	{
		return friends;
	}
	
	public FriendArray getAssociatedFriends()
	{
		return associatedFriends;
	}
	
	public String getGroupName()
	{
		return groupName;
	}
	
	public void setGroupName(String newName)
	{
		groupName = newName;
	}
	
	public void AI()
	{ /* Expand to include all friend within sight */
		if(World.PrintToOutFile)
			World.outFile.println("************************* AI ID:"+this.getID() + "***********************************");
		if(this.isAlive)
		{
			int num;
			Random rGen = new Random();
			if(CheckForFriends())
				
			{ // if there is a friend detected
				Inhabitant tempInhab = GetClosestFriend();
				if (tempInhab != null)
				{
					num = rGen.nextInt(100);
					if(num<=5)
					{
						AI_RandomMove();
					}
					MoveToward(tempInhab.getX(), tempInhab.getY());
				}
			}
			else
			{
				AI_RandomMove();
			}
			CheckFriends();
		}
	}
	
	
	public void AI_RandomMove()
	{
		boolean moved = false;
		//Random rGen = new Random();
		while (!moved)
		{
			int n = MoveGeneration();
			
			switch(n)
			{
			case 0:
				moved = true;
				break;
			case 1:
				moved = MoveUpRight();//Move(x_pos+1,y_pos+1);
				break;
			case 2:
				moved = MoveRight();//Move(x_pos+1,y_pos);
				break;
			case 3:
				moved = MoveDownRight();//Move(x_pos+1,y_pos-1);
				break;
			case 4:
				moved = MoveUp();//Move(x_pos,y_pos+1);
				break;
			case 5:
				moved = MoveDown();//Move(x_pos,y_pos-1);
				break;
			case 6:
				moved = MoveUpLeft();//Move(x_pos-1,y_pos+1);
				break;
			case 7:
				moved = MoveLeft();//Move(x_pos-1,y_pos);
				break;
			case 8:
				moved = MoveDownLeft();//Move(x_pos-1,y_pos-1);
				break;
			default:
				break;
			}
		}
	}
	
	public boolean MoveUpRight()
	{
		return Move(x_pos+1,y_pos+1);
	}
	public boolean MoveRight()
	{
		return Move(x_pos+1,y_pos);
	}
	public boolean MoveDownRight()
	{
		return Move(x_pos+1,y_pos-1);
	}
	
	public boolean MoveUpLeft()
	{
		return Move(x_pos-1,y_pos+1);
	}
	public boolean MoveLeft()
	{
		return Move(x_pos-1,y_pos);
	}
	public boolean MoveDownLeft()
	{
		return Move(x_pos-1,y_pos-1);
	}
	
	public boolean MoveUp()
	{
		return Move(x_pos,y_pos+1);
	}

	public boolean MoveDown()
	{
		return Move(x_pos,y_pos-1);
	}
	
	public int MoveGeneration()
	{
		int max=0;
		int[] direction={0,0,0,0,0,0,0,0,0}; // stay, upright,right,downright,up,down,upleft,left,downleft
		Random rGen = new Random();
		for(int i=0; i<direction.length; i++)
		{
			direction[i] = rGen.nextInt(100);
		}
		// find max
		for(int i=1; i<direction.length; i++)
		{
			if(direction[i] > direction[max]){
				max = i;
			}
			else if(direction[i] == direction[max])
			{
				if(rGen.nextBoolean())
				{
					max = i;
				}
			}
		}
		return max;
	}
	
	public boolean Move(int x, int y)
	{ 
		// check if the move is legal
		if(x<World.environGrid[0].length && y<World.environGrid.length && x>=0 && y>=0)
		{
			if(Math.abs(x-x_pos)<=inhabStats.speed && Math.abs(y-y_pos)<=inhabStats.speed && World.environGrid[y][x].isHabitable && !CollisionDetect(x,y))
			{
				x_pos = x;
				y_pos = y;
				return true;
			}
		}
		return false;
	}
	
	public boolean CollisionDetect(int x, int y)
	{
		for(int i=0; i<Population.popArray.length; i++)
		{
			if(Population.InhabitantCollision(x, y))
			{
				return true; 
			}
		}
		return false;
	}
	
	public boolean CheckForFriends()
	{
		for(int i=0; i<Population.popArray.length; i++)
		{
			double distance = DistanceToFriend(Population.popArray[i]);
			if(distance>0 && distance<= inhabStats.sightRange)
			{// friend detected
				return true;
			}
		}
		return false;
	}
	
	public boolean MoveToward(int x, int y)
	{
		if(x_pos < x && y_pos < y)
		{
			if(Move(x_pos+inhabStats.speed, y_pos+inhabStats.speed))	
				return true;
			else
				return false;
		}
		else if(x_pos < x && y_pos > y)
		{
			if(Move(x_pos+inhabStats.speed, y_pos-inhabStats.speed))
				return true;
			else
				return false;
		}
		else if(x_pos < x && y_pos == y)
		{
			if(Move(x_pos+inhabStats.speed, y_pos))
				return true;
			else
				return false;
		}
		else if(x_pos == x && y_pos > y)
		{
			if(Move(x_pos, y_pos-inhabStats.speed))
				return true;
			else
				return false;
		}
		else if(x_pos == x && y_pos < y)
		{
			if(Move(x_pos, y_pos+inhabStats.speed))
				return true;
			else
				return false;
		}
		else if(x_pos > x && y_pos > y)
		{
			if(Move(x_pos-inhabStats.speed, y_pos-inhabStats.speed))
				return true;
			else 
				return false;
		}
		else if(x_pos > x && y_pos == y)
		{
			if(Move(x_pos-inhabStats.speed, y_pos))
				return true;
			else
				return false;
		}
		else if(x_pos > x && y_pos < y)
		{
			if(Move(x_pos-inhabStats.speed, y_pos+inhabStats.speed))
				return true;
			else
				return false;
		}
		return false;
	}
	
	public Inhabitant GetClosestFriend()
	{
		double distance = inhabStats.sightRange+1, newDistance;
		int location = -1;
		Inhabitant tempInhab = null;
		for(int i=0; i<Population.popArray.length; i++)
		{
			newDistance = DistanceToFriend(Population.popArray[i]);
			if(newDistance <= inhabStats.sightRange && newDistance > 0)
			{
				if(newDistance < distance)
				{
					distance = newDistance;
					location = i;
				}
			}
		}
		if(location != -1)
			tempInhab = Population.popArray[location];
		return tempInhab;
	}
	
	public double DistanceToFriend(Inhabitant friend)
	{
		double distance = -1;
		distance = Math.sqrt(Math.pow(Math.abs(x_pos - friend.getX()) , 2) + Math.pow(Math.abs(y_pos - friend.getY()) , 2)); 
		return distance;
	}
	
	public void Die()
	{// set isAlive to false and remove from friend lists
		isAlive = false;
	}
	
	/**
	 * Tested
	 * @return
	 */
	public void FindNeighbors()
	{
		double distance=0;
		for(int i=0; i<Population.popArray.length; i++)
		{
			
			distance = DistanceToFriend(Population.popArray[i]);
			//World.outFile..println(i+": "+distance+"          "+findDuplicate(Population.popArray[i].getID()));
			//World.outFile.println("0: ID:" + this.getID() + " " + friends.ReportFriends());
			
			if(distance <= Math.sqrt(2) && !friends.findDuplicate(Population.popArray[i].getID()) && Population.popArray[i].getID()!= this.getID() && Population.popArray[i].isAlive)
			{
				//World.outFile..println("ADD"+Population.popArray[i].getID() + " to " + this.getID());
				friends.AddFriend(this,Population.popArray[i]);
			}
			//World.outFile.println("0.5: ID:" + this.getID() + " " + friends.ReportFriends());
			//else if!
			if (distance > Math.sqrt(2) && friends.findDuplicate(Population.popArray[i].getID()) && Population.popArray[i].getID()!= this.getID())
			{
				//World.outFile..println("REMOVE "+Population.popArray[i].getID() + " from " + this.getID());
				friends.RemoveFriend(Population.popArray[i].getID());
			}
		}
	}
	
	/**
	 * WIP - find associated neighbors AKA get the neighbors of neighbors
	 */
	public	void FindAssociatedNeighbors()
	{
		associatedFriends = new FriendArray();
		if(World.PrintToOutFile)
			World.outFile.println("assoc start count:"+associatedFriends.getLength());
		RunAssociationConnecter(this.getAssociatedFriends());
		//World.outFile.println("Removing self "+this.getID());
		associatedFriends.RemoveFriend(this.getID());
	}
	
	public Inhabitant[]RunAssociationConnecter(FriendArray aF0) // JAVA IS PASS BY REFERENCE!!! LIARS!
	{
		//associatedFriends = new FriendArray();
		FindNeighbors();
		Inhabitant[] tempInhab = new Inhabitant[0];
		
		for(int i=0; i<friends.getLength(); i++)
		{
			//World.outFile.println("1:"+this.getID()+"    friends:"+this.getFriends().ReportFriends());
			if(!aF0.findDuplicate(friends.getFriends()[i].getID()) && friends.getFriends()[i].isAlive)
			{
				//World.outFile..println("Added assocFriend:"+friends.getFriends()[i].getID());
				aF0.AddFriend(this, friends.getFriends()[i]);
				tempInhab = friends.getFriends()[i].RunAssociationConnecter(aF0);
				for(int j=0; j<tempInhab.length; j++)
				{
					if(!aF0.findDuplicate(tempInhab[j].getID()) && tempInhab[j].isAlive)// && tempInhab[j].getID() != this.getID())
					{
						if(World.PrintToOutFile)
							World.outFile.println("Added assocFriend2:"+tempInhab[j].getID());
						aF0.AddFriend(this, tempInhab[j]);
					}
				}
			}
		}
		return aF0.getFriends();
	}
	
	public void CheckFriends()
	{
		for(int i=0; i<friends.getLength(); i++)
		{			if(!friends.getFriends()[i].isAlive)
			{
				if(World.PrintToOutFile)
					World.outFile.println("Removing friend:"+ friends.getFriends()[i].getID() + " from " + this.getID() + " b/c of death!");
				friends.RemoveFriend(friends.getFriends()[i].getID());
			}
			else if(friends.getFriends()[i].DistanceToFriend(this)<=Math.sqrt(2))
			{// still a friends
				friends.FriendSpotted(i);
			}
			else
			{// moved out of range	
				if(World.PrintToOutFile)
					World.outFile.println("Removing friend:"+ friends.getFriends()[i].getID() + " from " + this.getID() + " b/c of range!");
				friends.RemoveFriend(friends.getFriends()[i].getID());
			}
		}
		FindAssociatedNeighbors();
	}

	
}
